if !exists(param.A)
  M291 S1 T0 R"Use Auto Calibration macro" P"Using separate calibration macros may cause issues.<br>Please use the Auto Calibration macro"
  abort "Error: Auto Calibration macro required."



var x_test_pos = -90
var y_test_pos = -101
var tbl_pos_rear = 170
var tbl_pos_front = var.y_test_pos
var add_z_offset = -0.070
var probing_safety_margin = 6  ; Safety margin in mm above Z datum for nozzle probing
var max_nozzle_height_error = 0.05  ; Maximum allowed nozzle height deviation in mm

; ===== Test diviation =====
M561 ; clear any bed transform
G29 S2 ; Clear height map

; Safety check: Ensure build plate is at or above Z50 before probe attachment
if move.axes[2].machinePosition < 50
  G90
  G1 Z50 F6000

M98 R1 P"0:/sys/attachedcheck.g" ; make sure probe is conected, pick if negative and leave relay active

M204 T5000                                 ; set accelerations


; Precise Bed Leveling - using symmetrical positions for IDEX
M558 K0 P8 C"1.io4.in" H5 F300 T18000 A3
M98 P"0:/sys/user/actions/ProbeOffset.g"
G1 U999 F18000 ; Move U - carriage off the way
G30 P0 X0 Y{var.tbl_pos_rear} Z-99999    ; probe at rear center first
if result !=0
  M98 P"0:/sys/led/fault.g"
  echo >>"0:/sys/eventlog.txt" "Error: G32 failed"
  abort "Error: G32 failed"
G30 P1 X{-var.x_test_pos} Y{var.tbl_pos_front} Z-99999    ; probe at front right
if result !=0
  M98 P"0:/sys/led/fault.g"
  echo >>"0:/sys/eventlog.txt" "Error: G32 failed"
  abort "Error: G32 failed"
G30 P2 X{var.x_test_pos} Y{var.tbl_pos_front} Z-99999 S3 ; probe at front left and report adjustments needed
if result !=0
  M98 P"0:/sys/led/fault.g"
  echo >>"0:/sys/eventlog.txt" "Error: G32 failed"
  abort "Error: G32 failed"

M204 T10000                                 ; set accelerations

; Measure Z datum at left test position with probe
G90
G1 X{var.x_test_pos + sensors.probes[0].offsets[0]} Y{var.y_test_pos - sensors.probes[0].offsets[1]}
G38.2 K0 Z0
G4 P260
var z_datum = move.axes[2].machinePosition
var probe_height = var.z_datum - global.zOffset + var.probing_safety_margin
G91
G1 Z10

M558 K0 P8 C"1.io4.in" H5 F300 T18000 A3
M98 P"0:/sys/user/actions/ProbeOffset.g"

M204 T5000                                 ; set accelerations

M98 P"0:/sys/detachedcheck.g"

M98 P"0:/sys/nozzlewipe.g" T0

M42 P4 S1                ; Turn on relay

; ===== Measure at edge positions (left and right sides) =====
G90
G1 X{var.x_test_pos} Y{var.tbl_pos_front} Z{var.probe_height} F18000

G91
M98 P"0:/sys/nozzleprobe.g" Z1
var lll = move.axes[2].machinePosition
G1 Z5

echo "Left head pos at edge = "^{var.lll}^" mm"

G90
G1 X-999 F18000

M98 P"0:/sys/nozzlewipe.g" T1

G90
G1 X-999 F18000

G1 U{abs(var.x_test_pos) - global.uOffset} Y{var.tbl_pos_front - global.yOffset}

G90
G1 Z{var.probe_height}
M98 P"0:/sys/nozzleprobe.g" Z10
var rrr = move.axes[2].machinePosition
G1 Z10

G90
G1 X-999 U999 F18000

var ddd = {var.lll - var.rrr}
echo "Right head pos at edge = "^{var.rrr}^" mm"
echo "Edge deviation = "^{var.ddd}^" mm"

if abs(var.ddd) < var.max_nozzle_height_error ; ===If delta is less than allowed value===
  echo "Good, edge deviation is within tolerance: "^{var.ddd}^" mm"
  
else ; ===If delta is more than allowed value===
  M98 P"0:/sys/led/pause.g"
  var calMsg = "This calibration is only needed for Mirror and Duplicate mode."
  set var.calMsg = var.calMsg ^ "<br><br>If you don't plan to use either mode, skip this manual "
  set var.calMsg = var.calMsg ^ "adjustment and proceed automatically."
  set var.calMsg = var.calMsg ^ "<br><br>You can return to this step later before printing "
  set var.calMsg = var.calMsg ^ "in Mirror or Duplicate mode."
  M291 S4 K{"Calibrate","Skip","Cancel"} R{"Nozzles deviated by "^var.ddd^" mm"} P{var.calMsg}

  if input == 0 ; User chose "Calibrate"
    M291 S2 R"Calibration Process" P"You will be asked to: Loosen heat break and lower it. The build plate will push the nozzle up to match the other nozzle's level.<br>Click ""OK"" when ready to start."
    ;====Left is Lower====
    if var.ddd > 0
      G90
      G1 U-100 X-999 Z150 F18000
      M400

  
      M291 S2 R"Step 1: Loosen the Heatbreak" P"Use a 2mm hex screwdriver to loosen the heat break of the Right nozzle so the heat block can slide up and down.<br>Caution: HOT!"
      G1S1
      M291 S2 R"Step 2: Lower the Heatblock" P"Lower the heat block 2-3mm (Push it with a screwdriver or pliers)<br>(Caution: HOT!)"
  
      G1 X-999 U{abs(var.x_test_pos) - global.uOffset} Y{var.tbl_pos_front - global.yOffset} F18000

      G90
      G1 Z5 F18000
      G1 Z{var.lll + var.add_z_offset} F240
  
      M400
      M291 S2 R"Tighten Heat Break for Right Tool" P"Securely tighten the heat break after adjustment.<br>Click ""OK"" when done."
  
      G90
      G1 U-100 Z150 F18000
      M400

      M291 S2 R"Confirm Tightening" P"Ensure the heat break is fully tightened to maintain alignment.<br>Click ""OK"" to confirm."

      G90
      G1 X-999 U999 F18000

    ;====Right is Lower====
    if var.ddd < 0
      G90
      G1 X100 U999 Z150 F18000    
      M400

      M291 S2 R"Step 1: Loosen the Heatbreak" P"Use a 2mm hex screwdriver to loosen the heat break of the Left nozzle so the heat block can slide up and down.<br>Caution: HOT!"
      G1S1
      M291 S2 R"Step 2: Lower the Heatblock" P"Lower the heat block 2-3mm (Push it with a screwdriver or pliers)<br>(Caution: HOT!)"
  
      G1 U999 X{var.x_test_pos} Y{var.tbl_pos_front} F18000

      G90
      G1 Z5 F18000
      G1 Z{var.rrr + var.add_z_offset} F240
  
      M400
      M291 S2 R"Tighten Heat Break for Left Tool" P"Securely tighten the heat break after adjustment.<br>Click ""OK"" when done."
  
      G90
      G1 X100 Z150 F18000
      M400


      M291 S2 R"Confirm Tightening" P"Ensure the heat break is fully tightened to maintain alignment.<br>Click ""OK"" to confirm."

      G90
      G1 X-999 U999 F18000
    M98 P"0:/sys/led/resetstatus.g"
    M98 P"0:/macros/System/Calibration/QC/AutoCal/Tool Height Auto Calibration" A1
    M99
    abort


  elif input == 1 ; User chose "Skip Calibration"
    M98 P"0:/sys/led/resetstatus.g"
    echo "Skipping calibration as per user request."

  elif input == 2 ; User chose "Cancel Calibration"
    M98 P"0:/sys/led/resetstatus.g"
    M568 P0 S0 R0
    M568 P1 S0 R0
    M568 P2 S0 R0
    M568 P3 S0 R0
    abort "Calibration cancelled by user."

; ===== Step 2: Measure at center position for RTZ offset (ALWAYS happens) =====
echo "Proceeding to center measurement for RTZ offset..."

M42 P4 S1                ; Turn on relay

M98 P"0:/sys/nozzlewipe.g" T0

G90
G1 X-999 U999 F18000

G1 X0 Y{var.y_test_pos} Z{var.probe_height} F18000

G91
M98 P"0:/sys/nozzleprobe.g" Z1
var mid_lll = move.axes[2].machinePosition
G1 Z5

echo "Left head pos at center = "^{var.mid_lll}^" mm"

; Recalculate probe height based on left tool touchdown position
set var.probe_height = var.mid_lll + var.probing_safety_margin

G90
G1 X-999 F18000

M98 P"0:/sys/nozzlewipe.g" T1

G90
G1 X-999 F18000

G1 U{0 - global.uOffset} Y{var.y_test_pos - global.yOffset} F18000

G90
G1 Z{var.probe_height}
M98 P"0:/sys/nozzleprobe.g" Z10
var mid_rrr = move.axes[2].machinePosition
G1 Z10

M42 P4 S0                ; Turn off relay

G90
G1 X-999 U999 F18000

var mid_dev = {var.mid_lll - var.mid_rrr}

echo "Right head pos at center = "^{var.mid_rrr}^" mm"
echo "Center deviation (RTZ offset) = "^{var.mid_dev}^" mm"

set global.tool1ZOffset = var.mid_dev

; Generate Tool-1-ZOffset.g
echo >"0:/sys/user/variables/Tool-1-ZOffset.g" "set global.tool1ZOffset = "^{var.mid_dev}

; Generate ToolOffset.g
echo >"0:/sys/user/actions/ToolOffset.g" "G10 P1 U"^{global.uOffset}^" Y"^{global.yOffset}^" Z"^{global.tool1ZOffset}

; Apply the offsets
G10 P1 U{global.uOffset} Y{global.yOffset} Z{global.tool1ZOffset}

M291 S1 T20 R"Tool Height Calibration Successful" P{"Mirror Mode (edge deviation): " ^ var.ddd ^ " mm<br>Multi-Color Mode (T1 offset): " ^ var.mid_dev ^ " mm"}